﻿#include "Hook.h"

#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_relational.hpp>
#include <glm/ext/matrix_transform.hpp>  // perspective, translate, rotate
#include <glm/gtc/constants.hpp>
#include <glm/mat4x4.hpp>         // mat4
#include <glm/trigonometric.hpp>  // radians

#include "../Utils/Utils.h"
#include "../HeliumClient/Commands/CommandsManager.h"
#include "../HeliumClient/Config/ConfigManager.h"
#include "../HeliumClient/Modules/ModulesManager.h"
#include "../Utils/DrawUtils.h"

bool Hooks::shouldRender{true};

std::set<C_Entity*> Hooks::entityList{};
std::mutex Hooks::entityList_mutex{};

__int64 Hooks::RMBManager{};
int Hooks::isThirdPerson{};
bool Hooks::shouldLocalPlayerBeImmobile{};
bool Hooks::isHUDHidden{};
int Hooks::tickCountNow{};
bool Hooks::isDigging{};
std::u8string Hooks::lastAutoCompleteStr{};
bool Hooks::isTicked{};
fast_io::ip Hooks::serverIp{};

FuncHook Hooks::setPosHook{};
FuncHook Hooks::Actor_baseTickHook{};
FuncHook Hooks::Player_tickWorldHook{};
FuncHook Hooks::ClientInstanceScreenModel_sendChatMessageHook{};
FuncHook Hooks::UIScene_setupAndRenderHook{};
FuncHook Hooks::UIScene_renderHook{};
FuncHook Hooks::RenderTextHook{};
FuncHook Hooks::inventoryScreen__tickHook{};
FuncHook Hooks::Dimension_getFogColorHook{};
FuncHook Hooks::Dimension_getTimeOfDayHook{};
FuncHook Hooks::Dimension_getSunIntensityHook{};
FuncHook Hooks::ChestBlockActor_tickHook{};
FuncHook Hooks::Actor_lerpMotionHook{};
FuncHook Hooks::playerCallBack_Hook{};
FuncHook Hooks::AppPlatform_getGameEditionHook{};
FuncHook Hooks::PleaseAutoCompleteHook{};
FuncHook Hooks::LoopbackPacketSender_sendToServerHook{};
FuncHook Hooks::LevelRendererPlayer_getFovHook{};
FuncHook Hooks::MultiLevelPlayer_tickHook{};
FuncHook Hooks::GameMode_startDestroyBlockHook{};
FuncHook Hooks::GameMode_stopDestroyBlockHook{};
FuncHook Hooks::HIDController_keyMouseHook{};
FuncHook Hooks::BlockLegacy_getRenderLayerHook{};
FuncHook Hooks::BlockLegacy_getLightEmissionHook{};
FuncHook Hooks::LevelRenderer_renderLevelHook{};
FuncHook Hooks::ClickFuncHook{};
FuncHook Hooks::MoveInputHandler_tickHook{};
FuncHook Hooks::ChestScreenController_tickHook{};
FuncHook Hooks::GetGammaHook{};
FuncHook Hooks::Actor_isInWaterHook{};
FuncHook Hooks::JumpPowerHook{};
FuncHook Hooks::RMBManagerThingyHook{};
FuncHook Hooks::MinecraftGame_onAppSuspendedHook{};
FuncHook Hooks::Actor_ascendLadderHook{};
FuncHook Hooks::Actor_swingHook{};
FuncHook Hooks::Actor_lookAtHook{};
FuncHook Hooks::Actor_startSwimmingHook{};
FuncHook Hooks::RakNetInstance_tickHook{};
FuncHook Hooks::GameMode_getPickRangeHook{};
FuncHook Hooks::GameMode_attackHook{};
FuncHook Hooks::ConnectionRequest_createHook{};
FuncHook Hooks::DirectoryPackAccessStrategy__isTrustedHook{};
FuncHook Hooks::SkinRepository___loadSkinPackHook{};
FuncHook Hooks::toStyledStringHook{};
FuncHook Hooks::Mob__isImmobileHook{};
FuncHook Hooks::Actor__setRotHook{};
FuncHook Hooks::getDestroySpeedHook{};
FuncHook Hooks::InventoryTransactionManager__addActionHook{};
FuncHook Hooks::actorDieHook{};
FuncHook Hooks::ChatLogHook{};
FuncHook Hooks::forceThirdPersonHook{};
FuncHook Hooks::Actor__DestructorHook{};
FuncHook Hooks::play_inputmodeHook{};
FuncHook Hooks::getEditionHook{};

FuncHook Hooks::lambdaHooks{};


void Hooks::Init() {
	Logger::LogC(LogGrade::info, u8"Setting up Hooks...");

	const uintptr_t base = Utils::Utils::getBase();

	Logger::LogC(LogGrade::info, u8"MineCraft Base Address is ", fast_io::mnp::addrvw(base));

	const auto start_hook_time{fast_io::posix_clock_gettime(fast_io::posix_clock_id::process_cputime_id)};

	// GameMode::vtable
	{
		const uintptr_t GameMode_sigOffset = base + 31416250;
		const int offset = *reinterpret_cast<int*>(GameMode_sigOffset + 3);
		uintptr_t** gameModeVtable = reinterpret_cast<uintptr_t**>(GameMode_sigOffset + offset + /*length of instruction*/ 7);

		Hooks::GameMode_startDestroyBlockHook(gameModeVtable[1], &Hooks::GameMode_startDestroyBlock);

		Hooks::GameMode_stopDestroyBlockHook(gameModeVtable[4], &Hooks::GameMode_stopDestroyBlock);

		Hooks::GameMode_getPickRangeHook(gameModeVtable[10], &Hooks::GameMode_getPickRange);

		Hooks::GameMode_attackHook(gameModeVtable[14], &Hooks::GameMode_attack);
	}

	// BlockLegacy::vtable
	{
		intptr_t BlockLegacy_sigOffset = base + 39495407;  // BlockLegacy constructor
		int offset = *reinterpret_cast<int*>(BlockLegacy_sigOffset + 3);
		uintptr_t** blockLegacyVtable = reinterpret_cast<uintptr_t**>(BlockLegacy_sigOffset + offset + 7);
	}

	// LocalPlayer::vtable
	{
		uintptr_t LocalPlayer_sigOffset = base + 14193641;
		int offset = *reinterpret_cast<int*>(LocalPlayer_sigOffset + 3);
		uintptr_t** localPlayerVtable = reinterpret_cast<uintptr_t**>(LocalPlayer_sigOffset + offset + /*length of instruction*/ 7);

		Hooks::Actor_startSwimmingHook(localPlayerVtable[201], &Hooks::Actor_startSwimming);

		Hooks::Actor_ascendLadderHook(localPlayerVtable[339], &Hooks::Actor_ascendLadder);

		Hooks::Actor__setRotHook(localPlayerVtable[27], &Hooks::Actor__setRot);

		Hooks::Actor_swingHook(localPlayerVtable[219], &Hooks::Actor_swing);

		Hooks::JumpPowerHook(localPlayerVtable[345], &Hooks::JumpPower);  // jump from ground with movement proxy

		Hooks::setPosHook(localPlayerVtable[19], &Hooks::setPos);

		Hooks::Actor_baseTickHook(localPlayerVtable[49], &Hooks::Actor_baseTick);

		Hooks::Mob__isImmobileHook(localPlayerVtable[91], &Hooks::Mob__isImmobile);

		Hooks::Actor_isInWaterHook(localPlayerVtable[71], &Hooks::Actor_isInWater);

		Hooks::Player_tickWorldHook(localPlayerVtable[364], &Hooks::Player_tickWorld);

		Hooks::play_inputmodeHook(localPlayerVtable[435], &Hooks::inputmode_Hook);

		Hooks::Actor__DestructorHook(localPlayerVtable[14], &Hooks::Actor__Destructor);
	}

	// MoveInputHandler::vtable
	{
		uintptr_t MoveInputHandler_sigOffset = base + 43333020;
		int offset = *reinterpret_cast<int*>(MoveInputHandler_sigOffset + 3);
		uintptr_t** moveInputVtable = reinterpret_cast<uintptr_t**>(MoveInputHandler_sigOffset + offset + 7);

		Hooks::MoveInputHandler_tickHook(moveInputVtable[1], &Hooks::MoveInputHandler_tick);
	}

	// PackAccessStrategy vtables for isTrusted
	{
		uintptr_t PackAccessStrategy_sigOffset = base + 23541067;
		int offset = *reinterpret_cast<int*>(PackAccessStrategy_sigOffset + 3);
		uintptr_t** directoryPackVtable = reinterpret_cast<uintptr_t**>(PackAccessStrategy_sigOffset + offset + 7);

		Hooks::DirectoryPackAccessStrategy__isTrustedHook(directoryPackVtable[6], &Hooks::DirectoryPackAccessStrategy__isTrusted);
		/*
		uintptr_t PackAccessStrategy_sigOffset2 = base + 26867633;  // Was 48 8D 05 ?? ?? ?? ?? 48 89 03 49 8D 57 in 1.18.2
		int offset2 = *reinterpret_cast<int*>(PackAccessStrategy_sigOffset2 + 3);
		uintptr_t** directoryPackVtable2 = reinterpret_cast<uintptr_t**>(PackAccessStrategy_sigOffset2 + offset2 + 7);

		Hooks::ZipPackAccessStrategy__isTrustedHook(directoryPackVtable2[6], Hooks::ReturnTrue);
		Hooks::SkinRepository___checkSignatureFileInPack(base + 14469280, &Hooks::ReturnTrue);
		*/
	}
	{
		// uintptr_t** OnAppSuspendedVtable = reinterpret_cast<uintptr_t**>(FindSignature("48 8D 05 ? ? ? ? 49 89 46 ? 48 8D 05 ? ? ? ? 49 89 46 ? 48 8D 05 ? ? ? ? 49 89 46"));
		// Hooks::MinecraftGame_onAppSuspendedHook(OnAppSuspendedVtable[1], &Hooks::MinecraftGame_onAppSuspended);
	}
	// Signature
	// vtables better than sigs

	{
		void* _sendChatMessage = reinterpret_cast<void*>(base + 12984784);
		Hooks::ClientInstanceScreenModel_sendChatMessageHook(_sendChatMessage, &Hooks::ClientInstanceScreenModel_sendChatMessage);

		void* _renderCtx = reinterpret_cast<void*>(base + 10695184);
		Hooks::RenderTextHook(_renderCtx, &Hooks::RenderText);
		// Hooks::RenderTextHook.enableHook();

		void* setupRender = reinterpret_cast<void*>(base + 10780208);
		Hooks::UIScene_setupAndRenderHook(setupRender, &Hooks::UIScene_setupAndRender);

		void* render = reinterpret_cast<void*>(base + 10781088);
		Hooks::UIScene_renderHook(render, &Hooks::UIScene_render);

		void* fogColorFunc = reinterpret_cast<void*>(base + 43912624);
		Hooks::Dimension_getFogColorHook(fogColorFunc, &Hooks::Dimension_getFogColor);

		void* timeOfDay = reinterpret_cast<void*>(base + 37730672);
		Hooks::Dimension_getTimeOfDayHook(timeOfDay, &Hooks::Dimension_getTimeOfDay);

		void* sunItensity = reinterpret_cast<void*>(base + 37719776);
		Hooks::Dimension_getSunIntensityHook(sunItensity, &Hooks::Dimension_getSunIntensity);

		void* chestTick = reinterpret_cast<void*>(base + 40599424);
		Hooks::ChestBlockActor_tickHook(chestTick, &Hooks::ChestBlockActor_tick);

		void* lerpFunc = reinterpret_cast<void*>(base + 32728960);
		Hooks::Actor_lerpMotionHook(lerpFunc, &Hooks::Actor_lerpMotion);

		void* getRenderLayer = reinterpret_cast<void*>(base + 39708992);
		Hooks::BlockLegacy_getRenderLayerHook(getRenderLayer, &Hooks::BlockLegacy_getRenderLayer);

		void* getLightEmission = reinterpret_cast<void*>(base + 39711888);
		Hooks::BlockLegacy_getLightEmissionHook(getLightEmission, &Hooks::BlockLegacy_getLightEmission);

		void* getGameEdition = reinterpret_cast<void*>(base + 18912448);
		Hooks::AppPlatform_getGameEditionHook(getGameEdition, &Hooks::AppPlatform_getGameEdition);

		uintptr_t** packetSenderVtable = reinterpret_cast<uintptr_t**>(*(uintptr_t*)GameData::getClientInstance()->loopbackPacketSender);
		Hooks::LoopbackPacketSender_sendToServerHook(packetSenderVtable[2], &Hooks::LoopbackPacketSender_sendToServer);

		void* getFov = reinterpret_cast<void*>(base + 20128432);
		Hooks::LevelRendererPlayer_getFovHook(getFov, &Hooks::LevelRendererPlayer_getFov);

		void* getRMBHook = reinterpret_cast<void*>(base + 8220704);
		Hooks::RMBManagerThingyHook(getRMBHook, &Hooks::RMBManagerThingy);

		void* tick_entityList = reinterpret_cast<void*>(base + 15976256);
		Hooks::MultiLevelPlayer_tickHook(tick_entityList, &Hooks::MultiLevelPlayer_tick);

		void* keyMouseFunc = reinterpret_cast<void*>(base + 20743088);
		Hooks::HIDController_keyMouseHook(keyMouseFunc, &Hooks::HIDController_keyMouse);

		void* renderLevel = reinterpret_cast<void*>(base + 21368080);
		Hooks::LevelRenderer_renderLevelHook(renderLevel, &Hooks::LevelRenderer_renderLevel);

		void* playerCallBackHook = reinterpret_cast<void*>(base + 32726464);
		Hooks::playerCallBack_Hook(playerCallBackHook, &Hooks::playerCallBack);

		void* clickHook = reinterpret_cast<void*>(base + 47566016);
		Hooks::ClickFuncHook(clickHook, &Hooks::ClickFunc);

		void* chestScreenControllerTick = reinterpret_cast<void*>(base + 12088896);
		Hooks::ChestScreenController_tickHook(chestScreenControllerTick, &Hooks::ChestScreenController_tick);

		void* autoComplete = reinterpret_cast<void*>(base + 10023232);
		Hooks::PleaseAutoCompleteHook(autoComplete, &Hooks::PleaseAutoComplete);

		void* fullbright = reinterpret_cast<void*>(base + 13254512);
		Hooks::GetGammaHook(fullbright, &Hooks::GetGamma);

		void* RakNetInstance__tick = reinterpret_cast<void*>(base + 25658208);
		Hooks::RakNetInstance_tickHook(RakNetInstance__tick, &Hooks::RakNetInstance_tick);

		void* ConnectionRequest__create = reinterpret_cast<void*>(base + 24251984);
		Hooks::ConnectionRequest_createHook(ConnectionRequest__create, &Hooks::ConnectionRequest_create);

		void* ForceThirdPerson = reinterpret_cast<void*>(base + 0xCA3D40);  // 48 83 ec ? 48 8b 01 48 8d 54 24 ? 41 b8 ? ? ? ? ff 50 ? 48 8b 10 48 85 d2 74 ? 48 8b 42 ? 48 8b 88 ? ? ? ? 48 85 c9 74 ? e8 ? ? ? ? 48 83 c4 ? c3 8b 42 ? 48 83 c4 ? c3 e8 ? ? ? ? cc cc cc cc cc cc cc cc cc cc cc cc 48 83 ec ? 48 8b 01 48 8d 54 24 ? 41 b8 ? ? ? ? ff 50 ? 48 8b 10 48 85 d2 74 ? 48 8b 42 ? 48 8b 88 ? ? ? ? 48 85 c9 74 ? e8 ? ? ? ? 48 83 c4 ? c3 8b 42 ? 48 83 c4 ? c3 e8 ? ? ? ? cc cc cc cc cc cc cc cc cc cc cc cc 48 83 ec ? 48 8b 05 ? ? ? ? 48 33 c4 48 89 44 24 ? 48 8b 01 41 b8 ? ? ? ? 88 54 24 ? 48 8d 54 24 ? ff 50 ? 48 8b 08 48 85 c9 74 ? 48 8d 54 24 ? e8 ? ? ? ? 48 8b 4c 24 ? 48 33 cc e8 ? ? ? ? 48 83 c4 ? c3 e8 ? ? ? ? cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 48 83 ec ? 48 8b 01 48 8d 54 24 ? 41 b8 ? ? ? ? ff 50 ? 48 8b 10 48 85 d2 74 ? 48 8b 42 ? 48 8b 88 ? ? ? ? 48 85 c9 74 ? e8 ? ? ? ? 48 83 c4 ? c3 0f b6 42 ? 48 83 c4 ? c3 e8 ? ? ? ? cc cc cc cc cc cc cc cc cc cc cc 48 83 ec ? 48 8b 01
		Hooks::forceThirdPersonHook(ForceThirdPerson, &Hooks::ForceThirdPersonLol);

		void* chatLogHook = reinterpret_cast<void*>(base + 9292416);
		Hooks::ChatLogHook(chatLogHook, &Hooks::chatLogHookFunc);

		void* actorDie = reinterpret_cast<void*>(base + 14228288);
		Hooks::actorDieHook(actorDie, &Hooks::onActorDie);

		void* destroySpeed = reinterpret_cast<void*>(base + 32438144);
		Hooks::getDestroySpeedHook(destroySpeed, &Hooks::getDestroySpeed);

		void* inventoryScreen = reinterpret_cast<void*>(base + 12212048);
		Hooks::inventoryScreen__tickHook(inventoryScreen, &Hooks::inventoryScreen__tick);

		void* AppSuspended = reinterpret_cast<void*>(base + 26671552);
		Hooks::MinecraftGame_onAppSuspendedHook(AppSuspended, &Hooks::MinecraftGame_onAppSuspended);

		// void* onEntityEliminate = reinterpret_cast<void*>(FindSignature("E8 ? ? ? ? F6 C3 01 74 0D BA B8 05 00 00"));
		// Hooks::onEntityEliminate(onEntityEliminate, &Hooks::onEntityEliminate_func);

#if 0
		void* _getSkinPack = reinterpret_cast<void*>(base + 14401760);
		Hooks::SkinRepository___loadSkinPackHook(_getSkinPack, &Hooks::SkinRepository___loadSkinPack);

		void* getEdition = reinterpret_cast<void*>(base + 44778400);
		Hooks::getEditionHook(getEdition, &Hooks::getEdition);

		void* addAction = reinterpret_cast<void*>(base + 33715424);
		Hooks::InventoryTransactionManager__addActionHook(addAction, &Hooks::InventoryTransactionManager__addAction);
#endif
	}

	void* levelRendererBobView = reinterpret_cast<void*>(base + 20129632);

	constexpr void (*bobViewHookF)(__int64, glm::mat4&, float) = [](__int64 _this, glm::mat4& matrix, float lerpT) -> void {
		static auto origFunc = Hooks::lambdaHooks.GetFastcall<void, __int64, glm::mat4&, float>();

		return origFunc(_this, matrix, lerpT);
	};

	Hooks::lambdaHooks(levelRendererBobView, bobViewHookF);

	Logger::LogC(LogGrade::info, u8"Hooks initialized! (process_cputime_id) takes: ", fast_io::posix_clock_gettime(fast_io::posix_clock_id::process_cputime_id) - start_hook_time, fast_io::mnp::chvw(u8's'));
}

void Hooks::Restore() {
	MH_DisableHook(MH_ALL_HOOKS);
}

void Hooks::Enable() {
	Logger::LogC(LogGrade::info, u8"Hook Enable!");
	MH_EnableHook(MH_ALL_HOOKS);
}

bool Hooks::ReturnTrue() {
	return true;
}

bool Hooks::ReturnFalse() {
	return false;
}

bool canSetPos_Hook = false;
void Hooks::setPos(C_Entity* ent, vec3_t& poo) {
	static auto func = Hooks::setPosHook.GetFastcall<void, C_Entity*, vec3_t&>();
	if (canSetPos_Hook) [[unlikely]]
		return func(ent, poo);

	return func(ent, poo);
}

void Hooks::Actor_baseTick(C_Entity* ent) {
	static auto oFunc = Hooks::Actor_baseTickHook.GetFastcall<void, C_Entity*>();
	C_LocalPlayer* player = GameData::getLocalPlayer();
	if (!player || !player->getlevel()) [[unlikely]] {
		// Logger::LogC(LogGrade::trace, std::source_location::current(), u8"player is nullptr");
		return oFunc(ent);
	}

	[[maybe_unused]] auto look = std::lock_guard(Hooks::entityList_mutex);

	static int tickCountThen = 0;
	if (Hooks::tickCountNow = *(int*)((uintptr_t)player->getlevel() + 0x690); Hooks::tickCountNow != tickCountThen) [[unlikely]] {
		Hooks::entityList.clear();
		tickCountThen = Hooks::tickCountNow;
		// Logger::LogC(LogGrade::trace, std::source_location::current(), u8"tickCountNow is : ", tickCountThen);
	}
	using std::operator""sv;
	if (GameData::getScreenName != u8"world_loading_progress_screen - joining_multiplayer_external_server"sv
		&& GameData::getScreenName != u8"nether_loading_progress_screen - nether"sv 
		&& GameData::getScreenName != u8"overworld_loading_progress_screen - nether"sv
		&& GameData::getScreenName != u8"theend_loading_progress_screen - nether"sv) [[likely]] {
		if (ent->isClientSide()) [[likely]] {
			// if (GameData::CheckPointer(ent) && GameData::CheckPointer(*reinterpret_cast<void**>(ent)) && GameData::CheckPointer((*reinterpret_cast<void***>(ent))[63])) {
			Hooks::entityList.insert(ent);
			//}
		}
	} else [[unlikely]] {
		Hooks::entityList.clear();
		[[maybe_unused]] auto lock = std::shared_lock(botlist_lock);
		botlist.clear();
	}

	return oFunc(ent);
}

void Hooks::RMBManagerThingy(__int64 a1, __int64 a2) {
	static auto oClick = Hooks::RMBManagerThingyHook.GetFastcall<void, __int64, __int64>();
	Hooks::RMBManager = a1;
	return oClick(a1, a2);
}

void* Hooks::Player_tickWorld(C_Player* _this, __int64 unk) {
	static auto oTick = Hooks::Player_tickWorldHook.GetFastcall<void*, C_Player*, __int64>();

	auto o = oTick(_this, unk);

	if (_this == GameData::getLocalPlayer()) [[unlikely]] {
		// TODO: refactor all modules to not use GameMode
		C_GameMode* gm = *reinterpret_cast<C_GameMode**>(reinterpret_cast<uintptr_t>(_this) + 0x1238);

		using std::operator""sv;
		if (GameData::getScreenName != u8"chat_screen"sv || GameData::isKeyDown(VK_DELETE) || GameData::isKeyDown(VK_BACK))
			Hooks::lastAutoCompleteStr.clear();

		GameData::updateGameData(gm);
		ModuleManager::onWorldTick(gm);
	}

	// if (GameData::getLocalPlayer() == nullptr) {
	//	Hooks::entityList.clear();
	// }

	return o;
}

void Hooks::ClientInstanceScreenModel_sendChatMessage(void* _this, TextHolder* text) {
	static auto oSendMessage = Hooks::ClientInstanceScreenModel_sendChatMessageHook.GetFastcall<void, void*, TextHolder*>();

	if (text->getTextLength() > 0) [[likely]] {
		auto message = text->getString();

		if (message.front() == CommandMgr::prefix) [[unlikely]] {
			CommandMgr::execute(message);
			return;
		} else if (message.front() == u8'.') [[unlikely]] {
			// maybe the user forgot his prefix, give him some helpful advice
			static bool helpedUser = false;
			if (!helpedUser) [[unlikely]] {
				helpedUser = true;

				switch (SettingManager::languageget) {
				case 0:
				default:
					C_GuiData::displayClientMessageC(
						Utils::TextColor::LIGHT_PURPLE,
						u8"[Helium] ",
						Utils::TextColor::RED,
						u8"The prefix you bind is: \" ",
						Utils::TextColor::YELLOW,
						fast_io::mnp::chvw(CommandMgr::prefix),
						Utils::TextColor::RED,
						u8" \", Enter \" ",
						Utils::TextColor::WHITE,
						u8".prefix",  // command name
						Utils::TextColor::RED,
						u8" \" to set it! (This message will only be displayed once)");
					break;
				case 1:
					C_GuiData::displayClientMessageC(
						Utils::TextColor::LIGHT_PURPLE,
						u8"[Helium] ",
						Utils::TextColor::RED,
						u8"您绑定的命令前缀是: \" ",
						Utils::TextColor::YELLOW,
						fast_io::mnp::chvw(CommandMgr::prefix),
						Utils::TextColor::RED,
						u8" \"，输入 \" ",
						Utils::TextColor::WHITE,
						u8".prefix",  // command name
						Utils::TextColor::RED,
						u8" \" 来设置！（这个消息只会显示一次）");
					break;
				case 2:
					C_GuiData::displayClientMessageC(
						Utils::TextColor::LIGHT_PURPLE,
						u8"[Helium] ",
						Utils::TextColor::RED,
						u8"您綁定的命令前綴是: \" ",
						Utils::TextColor::YELLOW,
						fast_io::mnp::chvw(CommandMgr::prefix),
						Utils::TextColor::RED,
						u8" \"，輸入 \" ",
						Utils::TextColor::WHITE,
						u8".prefix",  // command name
						Utils::TextColor::RED,
						u8" \" 來設置！（這個消息只會顯示一次）");
					break;
				case 3:
					C_GuiData::displayClientMessageC(
						Utils::TextColor::LIGHT_PURPLE,
						u8"[Helium] ",
						Utils::TextColor::RED,
						u8"バインドするコマンド プレフィックスは次のとおりです: \" ",
						Utils::TextColor::YELLOW,
						fast_io::mnp::chvw(CommandMgr::prefix),
						Utils::TextColor::RED,
						u8" \"，\" ",
						Utils::TextColor::WHITE,
						u8".prefix",  // command name
						Utils::TextColor::RED,
						u8" \" と入力してセットアップします。(このメッセージは一度だけ表示されます)");
					break;
					std::unreachable();
				}
			}
		}
		if (TranslateModule.isEnabled() && (TranslateEnum_TranslateModule.selected == 0 || TranslateEnum_TranslateModule.selected == 2)) {
#if 0
			auto transres = translate(message, false);
			if (transres.empty())
				return oSendMessage(_this, text);
			TextHolder_view temp{transres};
			return oSendMessage(_this, reinterpret_cast<TextHolder*>(&temp));
#else
			static constexpr void (*fun)(std::u8string_view, std::u8string_view) = [](std::u8string_view original, std::u8string_view res) -> void {
				if (res.empty())
					print(ServiceMessage, original);
				else
					print(ServiceMessage, res);
			};
			translate_asynchronous(std::u8string(message), false, fun);
			return;
#endif  // 0
		}
	}

	return oSendMessage(_this, text);
}

void Hooks::chatLogHookFunc(__int64 a1, TextHolder* msg, uint32_t a2) {
	static auto oChatLogFunc = Hooks::ChatLogHook.GetFastcall<void, __int64, TextHolder*, uint32_t>();

	auto& message = *reinterpret_cast<TextHolder_view*>(reinterpret_cast<uintptr_t>(msg) + 0x8);
	auto& name = *reinterpret_cast<TextHolder_view*>(reinterpret_cast<uintptr_t>(msg) + 0x48);

	{  // chat log
		if (name.getTextLength() == 0) {
			Logger::LogC(LogGrade::chat_log, message.getString());
		} else {
			Logger::LogC(LogGrade::chat_log, fast_io::mnp::chvw(u8'<'), name.getString(), u8"> ", message.getString());
		}
	}
	if (C_GuiData::isHeliumMessage) {
		C_GuiData::isHeliumMessage = false;
		// do sth
	} else {  
		// do sth
		if (TranslateModule.isEnabled() && (TranslateEnum_TranslateModule.selected == 1 || TranslateEnum_TranslateModule.selected == 2)) {
#if 0
			std::u8string_view str(message.getString());
			if (str.empty()) 
				return oChatLogFunc(a1, msg, a2);

			auto transres = translate(str, true);
			if (transres.empty())
				return oChatLogFunc(a1, msg, a2);

			oChatLogFunc(a1, msg, a2);
			C_GuiData::displayClientMessageC(Utils::TextColor::LIGHT_PURPLE,
											 u8"[Helium] ",
											 Utils::TextColor::YELLOW,
											 u8"(Translatation results) ",
											 Utils::TextColor::RESET,
											 transres);
			return;
#else
			std::u8string_view str(message.getString());
			if (str.empty())
				return oChatLogFunc(a1, msg, a2);

			oChatLogFunc(a1, msg, a2);
			
			static constexpr void (*fun)(std::u8string_view, std::u8string_view) = [](std::u8string_view original, std::u8string_view res) -> void {
				if (!res.empty() && res != original) {
					C_GuiData::displayClientMessageC(Utils::TextColor::LIGHT_PURPLE,
													 u8"[Helium] ",
													 Utils::TextColor::YELLOW,
													 u8"(Translatation results) ",
													 Utils::TextColor::RESET,
													 res);
				}
			};

			translate_asynchronous(std::u8string(str), true, fun);

			return;
#endif  // 0
		}
	}
	return oChatLogFunc(a1, msg, a2);
}

__int64 Hooks::UIScene_setupAndRender(C_UIScene* uiscene, __int64 screencontext) {
	const auto oSetup = Hooks::UIScene_setupAndRenderHook.GetFastcall<__int64, C_UIScene*, __int64>();
	Hooks::shouldRender = false;
	return oSetup(uiscene, screencontext);
}

__int64 Hooks::UIScene_render(C_UIScene* uiscene, __int64 screencontext) {  // 3 times
	static auto oFunc = Hooks::UIScene_renderHook.GetFastcall<__int64, C_UIScene*, __int64>();

	TextHolder alloc{};
	uiscene->getScreenName(&alloc);

	// auto aloc_str = alloc.getString();

	if (alloc.getTextLength() < 100) [[likely]] {
		// strcpy_s(Hooks::currentScreenName, alloc.getText());
		if (alloc.getString() != u8"toast_screen" && alloc.getString() != u8"debug_screen")
			GameData::getScreenName = alloc.getString();
	}

	if (!Hooks::shouldRender) {
		Hooks::shouldRender = alloc.getString() == u8"start_screen" || alloc.getString() == u8"hud_screen" || alloc.getString() == u8"inventory_screen";
	}

#ifdef _DEBUG
	static std::u8string lastScreenName{};
	if (GameData::getScreenName != lastScreenName) [[unlikely]] {
		lastScreenName = GameData::getScreenName;
		Logger::LogC(LogGrade::debug, u8"ScreenName changed to: ", GameData::getScreenName);
	}
#endif  // _DEBUG

#if 0
	static auto clickGuiMod = moduleMgr->getModule<ClickGuiMod>();
	if (clickGuiMod->isEnabled() && (strcmp(alloc.getText(), "pause_screen") == 0 || strcmp(alloc.getText(), "respawn_screen") == 0 || strcmp(alloc.getText(), "disconnect_screen") == 0)) {
		clickGuiMod->setEnabled(false);
		GameData::getClientInstance()->grabMouse();
	}

#endif  // 0

	alloc.alignedTextLength = 0;  // MineCraft will release the ptext itself
	return oFunc(uiscene, screencontext);
}

__int64 Hooks::RenderText(__int64 a1, C_MinecraftUIRenderContext* renderCtx) {
	static auto oFunc = Hooks::RenderTextHook.GetFastcall<__int64, __int64, C_MinecraftUIRenderContext*>();

	static const C_GuiData* const dat = GameData::getClientInstance()->getGuiData();
	// Logger::LogC(LogGrade::trace, std::source_location::current(), u8"renderCtx: ", renderCtx);
	DrawUtils::setCtx(renderCtx, dat);

	static uint8_t count = 0ui8;
	count++;

	if (count == 3) [[unlikely]]
		count = 0;
	else [[likely]]
		return oFunc(a1, renderCtx);

	// pre
	ModuleManager::onPreRender(renderCtx);
	DrawUtils::flush();
	__int64 return_val = oFunc(a1, renderCtx);

	// post
	ModuleManager::onPostRender(renderCtx);
	DrawUtils::flush();

	return return_val;
}

float* Hooks::Dimension_getFogColor(__int64 a1, float* color, __int64 brightness, float a4) {
	static auto oFunc = Hooks::Dimension_getFogColorHook.GetFastcall<float*, __int64, float*, __int64, float>();

	return oFunc(a1, color, brightness, a4);
}

float Hooks::Dimension_getTimeOfDay(__int64 a1, int a2, float a3) {
	static auto oFunc = Hooks::Dimension_getTimeOfDayHook.GetFastcall<float, __int64, int, float>();

	return oFunc(a1, a2, a3);
}

float Hooks::Dimension_getSunIntensity(__int64 a1, float a2, vec3_t* a3, float a4) {
	static auto oFunc = Hooks::Dimension_getSunIntensityHook.GetFastcall<float, __int64, float, vec3_t*, float>();

	return oFunc(a1, a2, a3, a4);
}

void Hooks::ChestBlockActor_tick(C_ChestBlockActor* a1, void* a2) {
	static auto oFunc = Hooks::ChestBlockActor_tickHook.GetFastcall<void, C_ChestBlockActor*, void*>();

	return oFunc(a1, a2);
}

void Hooks::Actor_lerpMotion(C_Entity* _this, vec3_t a2) {
	static auto oFunc = Hooks::Actor_lerpMotionHook.GetFastcall<void, C_Entity*, vec3_t>();

	return oFunc(_this, a2);
}

int Hooks::AppPlatform_getGameEdition(__int64 _this) {
	static auto oFunc = Hooks::AppPlatform_getGameEditionHook.GetFastcall<int, __int64>();

	return oFunc(_this);
}

void Hooks::PleaseAutoComplete(__int64 _this, __int64 a2, TextHolder* text, int a4) {
	static auto oFunc = Hooks::PleaseAutoCompleteHook.GetFastcall<void, __int64, __int64, TextHolder*, int>();

	if (GameData::getLocalPlayer() == nullptr) [[unlikely]]
		return oFunc(_this, a2, text, a4);

	auto text_str = text->getString();

	constexpr auto CommandsCount = CommandMgr::getCommandsCount();
	if constexpr (CommandsCount == 0)  // debug...
		return;

	if (!text_str.empty() && text_str.front() == CommandMgr::prefix) {
		fast_io::vector<std::u8string> cmdlist{};
		bool whetherToCompleteTheNextOne = false;
		{
			std::u8string temp{};
			for (size_t i = 1; i < text_str.length(); i++) {
				if (text_str[i] != u8' ') [[likely]]
					temp.push_back(text_str[i]);
				else [[unlikely]]
					cmdlist.emplace_back(std::move(temp));  // move == swap ???
			}
			if (!temp.empty())
				cmdlist.emplace_back(std::move(temp));  // move == swap ???
			else
				whetherToCompleteTheNextOne = true;
		}

		struct completeList_Struct {
			std::u8string cmd{};
			size_t wrong{};
			uint8_t type{}; // 0: Alphabetical order, 1 : Sequential, 2 : contain, 3 : Misspelling, 4 : wrong
			void* next{}; // sort

			completeList_Struct(std::u8string&& c, size_t w, uint8_t t, void* n = nullptr) noexcept : cmd(c), wrong(w), type(t), next(n) {}
			completeList_Struct(std::u8string& c, size_t w, uint8_t t, void* n = nullptr) noexcept : cmd(c), wrong(w), type(t), next(n) {}

			bool operator==(completeList_Struct& R) noexcept {
				if (type == R.type) {
					switch (type) {
					case 0:
					case 1:
						return cmd == R.cmd;
					case 2:
					case 3:
						return wrong == R.wrong && cmd == R.cmd;
					default:
						std::unreachable();
					}
				}
				return false;
			}

			std::strong_ordering operator<=>(completeList_Struct& R) noexcept {
				if (type != R.type) 
					return type <=> R.type;
				// type == R.type
				switch (type) {
				case 0:
				case 1:
					return cmd <=> R.cmd;
				case 2:
				case 3:
					if (wrong == R.wrong)
						return cmd <=> R.cmd;
					else
						return wrong <=> R.wrong;
				default:
					std::unreachable();
				}
			}
		};

		constexpr auto sortAscendingOrder = [](completeList_Struct& L, completeList_Struct& R)  -> bool {
			return L < R;
		};

		using syncFunc = void(__fastcall*)(TextHolder*, TextHolder*);
		static const syncFunc sync = reinterpret_cast<syncFunc>(Utils::Utils::getBase() + 16297920);

		const auto setTextAndReturn = [_this, a2, text, a4](std::u8string& t) {
			if (text->alignedTextLength >= 16) { // Modify heap memory
				text->textLength = t.size();
				Utils::Utils::patchBytes(text->pText, t.data(), text->textLength);
				sync(text, text);
				Hooks::lastAutoCompleteStr = std::move(t);  // MOVE!!!
				oFunc(_this, a2, text, a4);
			} else {
				text->setText(t);
				sync(text, text);
				Hooks::lastAutoCompleteStr = std::move(t);  // MOVE!!!
				oFunc(_this, a2, text, a4);
			}
		};

		static fast_io::vector<completeList_Struct> completeList{};
		static size_t completeListPlace{};

		static std::u8string concat{};  // = fast_io::u8concat(cmd->first, fast_io::mnp::chvw(u8' '));
		static uint8_t lastCompleteType{};

		if (Hooks::lastAutoCompleteStr == text_str) {
			switch (lastCompleteType) {
			case 1:
			case 3: {
				++completeListPlace;
				if (completeListPlace >= completeList.size()) [[unlikely]]  // go back to head
					completeListPlace = 0;
				std::u8string complete = fast_io::u8concat(fast_io::mnp::chvw(CommandMgr::prefix), completeList[completeListPlace].cmd);
				setTextAndReturn(complete);
				return;
			} break;
			case 2:
			case 4: {
				auto cmd = CommandMgr::NameTable.find(cmdlist.front());
				if (cmd == CommandMgr::NameTable.end())  // Check whether the instruction exists
					return oFunc(_this, a2, text, a4);
				++completeListPlace;
				if (completeListPlace >= completeList.size()) [[unlikely]]  // go back to head
					completeListPlace = 0;
				std::u8string complete = fast_io::u8concat(fast_io::mnp::chvw(CommandMgr::prefix), cmd->first, fast_io::mnp::chvw(u8' '), concat, completeList[completeListPlace].cmd);
				setTextAndReturn(complete);
				return;
			} break;
			default:
				std::unreachable();
			}
		} else if (cmdlist.empty()) {  // list commands and complete
			lastCompleteType = 1;

			completeList.clear();
			completeListPlace = 0;
			concat.clear();

			completeList.reserve(CommandsCount);
			for (size_t i = 0; i < CommandsCount; i++) 
				completeList.emplace_back_unchecked(std::u8string{CommandMgr::commandList[i]->_command}, 0, 0ui8, &CommandMgr::commandList[i]->_description.at(SettingManager::languageget));
			
			std::ranges::sort(completeList, sortAscendingOrder);  // sort

			C_GuiData::displayClientMessageC(Utils::TextColor::GRAY, u8"========================");
			for (auto& i : completeList)
				C_GuiData::displayClientMessageC(fast_io::mnp::chvw(CommandMgr::prefix), Utils::TextColor::GRAY, i.cmd, Utils::TextColor::WHITE, u8" - ", Utils::TextColor::YELLOW, *reinterpret_cast<std::u8string_view*>(i.next));

			std::u8string complete = fast_io::u8concat(fast_io::mnp::chvw(CommandMgr::prefix), completeList.front().cmd);
			setTextAndReturn(complete);
			return;
		} else {
			if (whetherToCompleteTheNextOne) {  // Complete The Next One (tree)
				lastCompleteType = 2;
				auto cmd = CommandMgr::NameTable.find(cmdlist.front());
				if (cmd == CommandMgr::NameTable.end())  // Check whether the instruction exists
					return oFunc(_this, a2, text, a4);

				completeList.clear();
				completeListPlace = 0;
				concat.clear();

				C_GuiData::displayClientMessageC(Utils::TextColor::GRAY, u8"========================");
				C_GuiData::displayClientMessageC(fast_io::mnp::chvw(CommandMgr::prefix), Utils::TextColor::GRAY, cmd->first, Utils::TextColor::WHITE, u8" - ", Utils::TextColor::YELLOW, cmd->second->_description.at(SettingManager::languageget));
				if (!cmd->second->_usage.empty())
					C_GuiData::displayClientMessageC(Utils::TextColor::BOLD, Utils::TextColor::RED, u8"Usage: ", Utils::TextColor::RESET, Utils::TextColor::ITALIC, Utils::TextColor::GRAY, cmd->second->_usage);

				fast_io::vector<aliasList_struct>* next = &cmd->second->aliasList;  // Resolved for the first time

				for (size_t j = 1; j < cmdlist.size(); j++) {
					const auto nextsize = next->size();
					for (size_t i = 0; i < nextsize; i++) {
						auto newnext{reinterpret_cast<fast_io::vector<aliasList_struct>*>(next_objects(next->operator[](i), &cmdlist, j))};
						if (newnext != nullptr) {
							next = newnext;
							concat.append(fast_io::u8concat(cmdlist[j], fast_io::mnp::chvw(u8' ')));
							break;
						}
						if (i == nextsize - 1) [[unlikely]] {  // not found
							concat.clear();
							return oFunc(_this, a2, text, a4);  // next = nullptr (return)
						}
					}
				}

				C_GuiData::displayClientMessageC(Utils::TextColor::DARK_GRAY, u8"------------------------");
				
				completeList.reserve(next->size());
				for (auto& al : *next) {
					C_GuiData::displayClientMessageC(fast_io::mnp::chvw(CommandMgr::prefix), Utils::TextColor::DARK_GRAY, cmd->first, fast_io::mnp::chvw(u8' '), concat, Utils::TextColor::GRAY, next_complete(al));
					completeList.emplace_back_unchecked(next_complete(al), 0, 0ui8);
				}
				std::u8string complete = fast_io::u8concat(fast_io::mnp::chvw(CommandMgr::prefix), cmd->first, fast_io::mnp::chvw(u8' '), concat, completeList.front().cmd);
				setTextAndReturn(complete);
				return;

			} else {
				constexpr auto Analyze = [](std::u8string_view input, std::u8string_view cmd) -> completeList_Struct {
					// Contains words
					if (auto f = cmd.find(input); f != std::u8string_view::npos) {
						if (f == 0)
							return completeList_Struct{std::u8string{cmd}, 0, 1};
						else {
							const size_t wrong = cmd.size() - input.size();
							if (wrong <= cmd.size() / 2 && cmd.size() - wrong <= cmd.size() * 3 / 2 && cmd.size() - wrong >= cmd.size() * 3 / 5)
								return completeList_Struct{std::u8string{cmd}, wrong, 2};
							else
								return completeList_Struct{std::u8string{cmd}, 0, 4};
						}
					} else if (auto f = input.find(cmd); f != std::u8string_view::npos) {
						const size_t wrong = input.size() - cmd.size();
						if (wrong <= cmd.size() / 2 && cmd.size() - wrong <= cmd.size() * 3 / 2 && cmd.size() - wrong >= cmd.size() * 3 / 5)
							return completeList_Struct{std::u8string{cmd}, wrong, 2};
						else
							return completeList_Struct{std::u8string{cmd}, 0, 4};
					}

					size_t wrong = CommandMgr::dp(input, cmd);

					if (wrong <= cmd.size() / 2) 
						return completeList_Struct{std::u8string{cmd}, wrong, 3};
					else 
						return completeList_Struct{std::u8string{cmd}, 0, 4};
				};

				completeList.clear();
				completeListPlace = 0;
				concat.clear();

				if (cmdlist.size() == 1) { // cmd
					lastCompleteType = 3;

					for (auto& cmds : CommandMgr::NameTable) {
						auto res = Analyze(cmdlist.front(), cmds.first);
						if (res.type != 4) {
							res.next = &cmds.second->_description.at(SettingManager::languageget);
							completeList.emplace_back(std::move(res));
						}
					}
					if (completeList.empty()) 
						return oFunc(_this, a2, text, a4);

					std::ranges::sort(completeList, sortAscendingOrder);  // sort

					C_GuiData::displayClientMessageC(Utils::TextColor::GRAY, u8"========================");
					for (auto& i : completeList)
						C_GuiData::displayClientMessageC(fast_io::mnp::chvw(CommandMgr::prefix), Utils::TextColor::GRAY, i.cmd, Utils::TextColor::WHITE, u8" - ", Utils::TextColor::YELLOW, *reinterpret_cast<std::u8string_view*>(i.next));
					
					std::u8string complete = fast_io::u8concat(fast_io::mnp::chvw(CommandMgr::prefix), completeList.front().cmd);
					setTextAndReturn(complete);
					return;
				} else {
					lastCompleteType = 4;

					auto cmd = CommandMgr::NameTable.find(cmdlist.front());
					if (cmd == CommandMgr::NameTable.end())  // Check whether the instruction exists
						return oFunc(_this, a2, text, a4);

					completeList.clear();
					completeListPlace = 0;
					concat.clear();

					C_GuiData::displayClientMessageC(Utils::TextColor::GRAY, u8"========================");
					C_GuiData::displayClientMessageC(fast_io::mnp::chvw(CommandMgr::prefix), Utils::TextColor::GRAY, cmd->first, Utils::TextColor::WHITE, u8" - ", Utils::TextColor::YELLOW, cmd->second->_description.at(SettingManager::languageget));
					if (!cmd->second->_usage.empty())
						C_GuiData::displayClientMessageC(Utils::TextColor::BOLD, Utils::TextColor::RED, u8"Usage: ", Utils::TextColor::RESET, Utils::TextColor::ITALIC, Utils::TextColor::GRAY, cmd->second->_usage);

					fast_io::vector<aliasList_struct>* next = &cmd->second->aliasList;  // Resolved for the first time

					for (size_t j = 1; j < cmdlist.size(); j++) {
						if (j != cmdlist.size() - 1) {
							const auto nextsize = next->size();
							for (size_t i = 0; i < nextsize; i++) {
								auto newnext{reinterpret_cast<fast_io::vector<aliasList_struct>*>(next_objects(next->operator[](i), &cmdlist, j))};
								if (newnext != nullptr) {
									next = newnext;
									concat.append(fast_io::u8concat(cmdlist[j], fast_io::mnp::chvw(u8' ')));
									break;
								}
								if (i == nextsize - 1) [[unlikely]] {  // not found
									concat.clear();
									return oFunc(_this, a2, text, a4);  // next = nullptr (return)
								}
							}
						} else {
							for (auto& i : *next) {
								auto res = Analyze(cmdlist[j], next_complete(i));
								if (res.type != 4) {
									completeList.emplace_back(std::move(res));
								}
							}
						}
					}

					if (completeList.empty())
						return oFunc(_this, a2, text, a4);

					std::ranges::sort(completeList, sortAscendingOrder);  // sort

					C_GuiData::displayClientMessageC(Utils::TextColor::DARK_GRAY, u8"------------------------");
					for (auto& al : completeList)
						C_GuiData::displayClientMessageC(fast_io::mnp::chvw(CommandMgr::prefix), Utils::TextColor::DARK_GRAY, cmd->first, fast_io::mnp::chvw(u8' '), concat, Utils::TextColor::GRAY, al.cmd);
					
					std::u8string complete = fast_io::u8concat(fast_io::mnp::chvw(CommandMgr::prefix), cmd->first, fast_io::mnp::chvw(u8' '), concat, completeList.front().cmd);
					setTextAndReturn(complete);
					return;
				}
			}
		}
	}

	return oFunc(_this, a2, text, a4);
}

void Hooks::LoopbackPacketSender_sendToServer(C_LoopbackPacketSender* a, C_Packet* packet) {
	static auto oFunc = Hooks::LoopbackPacketSender_sendToServerHook.GetFastcall<void, C_LoopbackPacketSender*, C_Packet*>();
	// Logger::LogC(LogGrade::trace, std::source_location::current(), u8"C_Packet: ", packet, u8" ,id: ", packet->getId(), u8" name: ", packet->getName()->getString());
	// if (packet->getId() == 1) {
	//	auto i = reinterpret_cast<LoginPacket*>(packet);
	// }

	bool cancelSend = false;
	ModuleManager::onSendPacket(packet, cancelSend);

	if (!cancelSend)
		oFunc(a, packet);
}

float Hooks::LevelRendererPlayer_getFov(__int64 _this, float a2, bool a3) {
	static auto oFunc = Hooks::LevelRendererPlayer_getFovHook.GetFastcall<float, __int64, float, bool>();

	return oFunc(_this, a2, a3);
}

void Hooks::MultiLevelPlayer_tick(C_EntityList* entityList) {
	static auto oFunc = Hooks::MultiLevelPlayer_tickHook.GetFastcall<void, C_EntityList*>();
	C_GameMode* gm = GameData::getCGameMode();
	if (gm != nullptr) [[likely]] {
		ModuleManager::onTick(gm);
	}

	return oFunc(entityList);
}

void Hooks::GameMode_startDestroyBlock(C_GameMode* _this, vec3_ti* a2, uint8_t face, void* a4, void* a5) {
	static auto oFunc = Hooks::GameMode_startDestroyBlockHook.GetFastcall<void, C_GameMode*, vec3_ti*, uint8_t, void*, void*>();
	Hooks::isDigging = true;  // return need to be false

	return oFunc(_this, a2, face, a4, a5);
}

void Hooks::GameMode_stopDestroyBlock(C_GameMode* _this, vec3_ti* a2) {
	static auto oFunc = Hooks::GameMode_stopDestroyBlockHook.GetFastcall<void, C_GameMode*, vec3_ti*>();
	Hooks::isDigging = false;

	return oFunc(_this, a2);
}

void Hooks::HIDController_keyMouse(C_HIDController* _this, void* a2, void* a3) {
	static auto oFunc = Hooks::HIDController_keyMouseHook.GetFastcall<void, C_HIDController*, void*, void*>();

	GameData::setHIDController(_this);
	Hooks::isTicked = true;

	return oFunc(_this, a2, a3);
}

int Hooks::BlockLegacy_getRenderLayer(C_BlockLegacy* a1) {
	static auto oFunc = Hooks::BlockLegacy_getRenderLayerHook.GetFastcall<int, C_BlockLegacy*>();

	return oFunc(a1);
}

__int8* Hooks::BlockLegacy_getLightEmission(C_BlockLegacy* _this, __int8* a2) {
	static auto oFunc = Hooks::BlockLegacy_getLightEmissionHook.GetFastcall<__int8*, C_BlockLegacy*, __int8*>();

	return oFunc(_this, a2);
}

__int64 Hooks::LevelRenderer_renderLevel(__int64 _this, __int64 a2, __int64 a3) {
	static auto oFunc = Hooks::LevelRenderer_renderLevelHook.GetFastcall<__int64, __int64, __int64, __int64>();


	DrawUtils::setGameRenderContext(a2);
	ModuleManager::onLevelRender();
	DrawUtils::setGameRenderContext(0);
	return oFunc(_this, a2, a3);
}

void Hooks::ClickFunc(__int64 a1, char a2, char a3, __int16 a4, __int16 a5, __int16 a6, __int16 a7, char a8) {
	static auto oFunc = Hooks::ClickFuncHook.GetFastcall<void, __int64, char, char, __int16, __int16, __int16, __int16, char>();

	return oFunc(a1, a2, a3, a4, a5, a6, a7, a8);
}

__int64 Hooks::MoveInputHandler_tick(C_MoveInputHandler* _this, C_Entity* a2) {
	static auto oFunc = Hooks::MoveInputHandler_tickHook.GetFastcall<__int64, C_MoveInputHandler*, C_Entity*>();

	__int64 ret = oFunc(_this, a2);
	ModuleManager::onMove(_this);
	return ret;
}

__int64 Hooks::ChestScreenController_tick(C_ChestScreenController* _this) {
	static auto oFunc = Hooks::ChestScreenController_tickHook.GetFastcall<__int64, C_ChestScreenController*>();

	return oFunc(_this);
}

float Hooks::GetGamma(uintptr_t* a1) {
	static auto oFunc = Hooks::GetGammaHook.GetFastcall<float, uintptr_t*>();

#ifdef _DEBUG
	uintptr_t** list{reinterpret_cast<uintptr_t**>(a1)};
	uint16_t obtainedSettings{};

	for (uint16_t i{3}; i < 450; i++) {
		if (list[i] == nullptr)
			continue;
		const uintptr_t* const info{*(uintptr_t**)((uintptr_t)list[i] + 8)};
		if (info == nullptr)
			continue;

		const TextHolder* translateName{(TextHolder*)((uintptr_t)info + 0x1D8)};
		const TextHolder* settingname{(TextHolder*)((uintptr_t)info + 0x188)};

		auto hasit = optionmap.find(std::u8string{translateName->getString()});

		if (hasit != optionmap.end()) {
			if (hasit->second.type == 0) {
				bool* temp = (bool*)((uintptr_t)list[i] + 16);
				*temp = hasit->second._bool;
			} else if (hasit->second.type == 1) {
				int* temp = (int*)((uintptr_t)list[i] + 24);
				*temp = hasit->second._int;
			} else if (hasit->second.type == 2) {
				float* temp = (float*)((uintptr_t)list[i] + 24);
				*temp = hasit->second._float;
			} else {
				fast_io::fast_terminate();
			}
			++obtainedSettings;
		}

		if (obtainedSettings == optionmap.size()) [[unlikely]]
			break;
	}
#endif  // _DEBUG

	return oFunc(a1);
}

bool Hooks::Actor_isInWater(C_Entity* _this) {
	static auto oFunc = Hooks::Actor_isInWaterHook.GetFastcall<bool, C_Entity*>();

	return oFunc(_this);
}

void Hooks::JumpPower(C_Entity* _this, float a2) {
	static auto oFunc = Hooks::JumpPowerHook.GetFastcall<void, C_Entity*, float>();

	return oFunc(_this, a2);
}

void Hooks::Actor_ascendLadder(C_Entity* _this) {
	static auto oFunc = Hooks::Actor_ascendLadderHook.GetFastcall<void, C_Entity*>();

	return oFunc(_this);
}

void Hooks::Actor_swing(C_Entity* _this) {
	static auto oFunc = Hooks::Actor_swingHook.GetFastcall<void, C_Entity*>();

	return oFunc(_this);
}

void Hooks::Actor_startSwimming(C_Entity* _this) {
	static auto oFunc = Hooks::Actor_startSwimmingHook.GetFastcall<void, C_Entity*>();

	return oFunc(_this);
}

void Hooks::RakNetInstance_tick(C_RakNetInstance* _this, __int64 a2, __int64 a3) {
	static auto oFunc = Hooks::RakNetInstance_tickHook.GetFastcall<void, C_RakNetInstance*, __int64, __int64>();

	GameData::setRakNetInstance(_this);

	return oFunc(_this, a2, a3);
}

float Hooks::GameMode_getPickRange(C_GameMode* _this, __int64 a2, char a3) {
	static auto oFunc = Hooks::GameMode_getPickRangeHook.GetFastcall<float, C_GameMode*, __int64, char>();

	ModuleManager::onGetPickRange();

	return oFunc(_this, a2, a3);
}

__int64 Hooks::GameMode_attack(C_GameMode* _this, C_Entity* ent) {
	static auto oFunc = Hooks::GameMode_attackHook.GetFastcall<__int64, C_GameMode*, C_Entity*>();

	ModuleManager::onAttack(ent);

#ifdef _DEBUG
	auto localPlayer = GameData::getLocalPlayer();
	if (localPlayer != nullptr) [[likely]] {
		using std::operator""sv;
		std::u8string_view type{};
		if (ent->isPlayer())
			type = u8"Player"sv;
		else if (ent->isAlive())
			type = u8"Alive"sv;
		else
			type = u8"Entity"sv;

		vec3_t pos = *localPlayer->getPos();
		vec3_t oldpos = *localPlayer->getPosOld();
		Logger::LogC(LogGrade::debug,
					 u8"<ATTACK ",
					 type,
					 u8"> Type = ",
					 ent->getType()->getString(),
					 u8", EntityTypeId = ",
					 ent->getEntityTypeId(),
					 u8", UniqueId = ",
					 ent->getUniqueId(),
					 u8", NameTag = ",
					 ent->getNameTag()->getString(),
					 u8". (Local Player) YawSpeed = ",
					 localPlayer->getYawSpeedInDegreesPerSecond() / *GameData::getClientInstance()->minecraft->timer,
					 u8", dist = ",
					 dist(pos, *ent->getPos()),
					 u8", Motion(X = ",
					 localPlayer->velocity.x,
					 u8", Y = ",
					 localPlayer->velocity.y,
					 u8", Z = ",
					 localPlayer->velocity.z,
					 u8") Yaw = ",
					 localPlayer->yaw,
					 u8", Pitch = ",
					 localPlayer->pitch,
					 u8", BodyYaw = ",
					 localPlayer->bodyYaw,
					 u8", OldBodyYaw = ",
					 localPlayer->oldBodyYaw,
					 u8", Position (X = ",
					 pos.x,
					 u8", Y = ",
					 pos.y,
					 u8", Z = ",
					 pos.z,
					 u8", Old Position (X = ",
					 oldpos.x,
					 u8", Y = ",
					 oldpos.y,
					 u8", Z = ",
					 oldpos.z,
					 u8") BPT = ",
					 dist(pos, oldpos),
					 u8" IsInWater = ",
					 fast_io::mnp::boolalpha(localPlayer->isInWater()),
					 u8" IsInLava = ",
					 fast_io::mnp::boolalpha(localPlayer->isInLava()),
					 u8" IsInWall = ",
					 fast_io::mnp::boolalpha(localPlayer->isInWall()));
	}
#endif  // _DEBUG

	return oFunc(_this, ent);
}

__int64 Hooks::ConnectionRequest_create(__int64 _this, __int64 privateKeyManager, void* a3, TextHolder* selfSignedId, TextHolder* serverAddress, __int64 clientRandomId, TextHolder* skinId, SkinData* skinData, __int64 capeData, CoolSkinData* coolSkinStuff, TextHolder* deviceId, int inputMode, int uiProfile, int guiScale, TextHolder* languageCode, bool sendEduModeParams, TextHolder* tenantId, __int64 unused, TextHolder* platformUserId, TextHolder* thirdPartyName, bool thirdPartyNameOnly, TextHolder* platformOnlineId, TextHolder* platformOfflineId, TextHolder* capeId) {
	static auto oFunc = Hooks::ConnectionRequest_createHook.GetFastcall<__int64, __int64, __int64, void*, TextHolder*, TextHolder*, __int64, TextHolder*, SkinData*, __int64, CoolSkinData*, TextHolder*, int, int, int, TextHolder*, bool, TextHolder*, __int64, TextHolder*, TextHolder*, bool, TextHolder*, TextHolder*, TextHolder*>();
	
#ifdef _DEBUG
	std::u8string serverAdd{serverAddress->getString()};
	if (auto temp = serverAdd.find(u8':'); temp != std::u8string::npos) {
		std::u8string_view dns(serverAdd.begin(), serverAdd.begin() + temp);
		if (!dns.empty() && temp < serverAdd.size() - 1) {
			std::u8string_view port(serverAdd.begin() + temp + 1, serverAdd.end());
			Hooks::serverIp = fast_io::to_ip(fast_io::native_dns_file(dns), fast_io::u8to<std::uint_least16_t>(port));

			Logger::LogC(LogGrade::debug, u8"Server Address: ", Hooks::serverIp);

		} else {
			Hooks::serverIp = fast_io::ip{};
		}
	} else {
		Hooks::serverIp = fast_io::ip{};
	}
#endif  // _DEBUG

	return oFunc(_this, privateKeyManager, a3, selfSignedId, serverAddress, clientRandomId, skinId, skinData, capeData, coolSkinStuff, deviceId, inputMode, uiProfile, guiScale, languageCode, sendEduModeParams, tenantId, unused, platformUserId, thirdPartyName, thirdPartyNameOnly, platformOnlineId, platformOfflineId, capeId);
}

void Hooks::InventoryTransactionManager__addAction(C_InventoryTransactionManager* a1, C_InventoryAction& a2) {
	static auto oFunc = Hooks::InventoryTransactionManager__addActionHook.GetFastcall<void, C_InventoryTransactionManager*, C_InventoryAction&>();

	return oFunc(a1, a2);
}

bool Hooks::DirectoryPackAccessStrategy__isTrusted(uintptr_t _this) {
	static auto oFunc = Hooks::DirectoryPackAccessStrategy__isTrustedHook.GetFastcall<bool, uintptr_t>();

	static uintptr_t** directoryPackAccessStrategyVtable = 0;

	if (!directoryPackAccessStrategyVtable) [[unlikely]] {
		const uintptr_t sigOffset = Utils::Utils::getBase() + 23541067;
		const int offset = *reinterpret_cast<int*>(sigOffset + 3);
		directoryPackAccessStrategyVtable = reinterpret_cast<uintptr_t**>(sigOffset + offset + 7);
	}

	if (_this == reinterpret_cast<uintptr_t>(directoryPackAccessStrategyVtable))
		return true;

	return oFunc(_this);
}

__int64 Hooks::SkinRepository___loadSkinPack(__int64 _this, __int64 pack, __int64 a3) {
	static auto oFunc = Hooks::SkinRepository___loadSkinPackHook.GetFastcall<__int64, __int64, __int64, __int64>();

	const auto res = (*(bool (**)(void))(**(__int64**)(pack + 8) + 48i64))();
	Logger::LogC(LogGrade::info, u8"SkinRepository___loadSkinPack: origin ", *(int*)((*(__int64*)pack) + 888i64), u8", is Trusted: ", res);
	*(int*)((*(__int64*)pack) + 910i64) = 2;  // Set pack origin to "2"

	return oFunc(_this, pack, a3);
}

GamerTextHolder* Hooks::toStyledString(__int64 strIn, GamerTextHolder* strOut) {
	static auto oFunc = Hooks::toStyledStringHook.GetFastcall<GamerTextHolder*, __int64, GamerTextHolder*>();

	return oFunc(strIn, strOut);
}

// extern void Helium_exit() ;
void Hooks::MinecraftGame_onAppSuspended(__int64 a) {
	static auto oFunc = Hooks::MinecraftGame_onAppSuspendedHook.GetFastcall<void, __int64>(); 
	// Logger::LogC(LogGrade::info, u8"GameSuspended");
	{
		[[maybe_unused]] const auto lock{std::lock_guard{Hooks::entityList_mutex}};
		Hooks::entityList.clear();
	}
	{
		[[maybe_unused]] const auto lock{std::shared_lock{player_recorder_mutex}};
		player_recorder.clear();
	}
	configMgr.saveConfig();
	ServiceMessage.name.clear();
	ServiceMessage.xboxid.clear();
	return oFunc(a);
}

bool Hooks::Mob__isImmobile(C_Entity* ent) {
	static auto oFunc = Hooks::Mob__isImmobileHook.GetFastcall<bool, C_Entity*>();

	const bool ret = oFunc(ent);

	if (ent == GameData::getLocalPlayer()) {
		Hooks::shouldLocalPlayerBeImmobile = ret;

		// do sth
	}

	return ret;
}

bool canSetRot_Hook = false;
void Hooks::Actor__setRot(C_Entity* _this, vec2_t& angle) {
	static auto oFunc = Hooks::Actor__setRotHook.GetFastcall<void, C_Entity*, vec2_t&>();
	if (canSetRot_Hook) [[unlikely]]
		return oFunc(_this, angle);

	return oFunc(_this, angle);
}

float Hooks::getDestroySpeed(C_Player* _this, C_Block& block) {
	static auto oFunc = Hooks::getDestroySpeedHook.GetFastcall<float, C_Player*, C_Block&>();

	float speed = oFunc(_this, block);

	return speed;
}

bool Hooks::playerCallBack(C_Player* lp, __int64 a2, __int64 a3) {
	static auto oFunc = Hooks::playerCallBack_Hook.GetFastcall<bool, C_Player*, __int64, __int64>();

	if (GameData::getLocalPlayer() != nullptr && lp == GameData::getLocalPlayer()) {
		ModuleManager::onPlayerTick(lp);
		using std::operator""sv;

		auto look = std::lock_guard(Hooks::entityList_mutex);

		if (!GameData::getLocalPlayer()->level || !*(&GameData::getLocalPlayer()->region + 1)) [[unlikely]] {
			Hooks::entityList.clear();
			// return oFunc(lp, a2, a3);
		} else if (GameData::getScreenName != u8"world_loading_progress_screen - joining_multiplayer_external_server"sv 
			&& GameData::getScreenName != u8"nether_loading_progress_screen - nether"sv 
			&& GameData::getScreenName != u8"overworld_loading_progress_screen - nether"sv
			&& GameData::getScreenName != u8"theend_loading_progress_screen - nether"sv) [[likely]]
			for (auto ent : GameData::getLocalPlayer()->level->getMiscEntityList())
				Hooks::entityList.insert(ent);
		else [[unlikely]]
			Hooks::entityList.clear();
	}

	return oFunc(lp, a2, a3);
}

void Hooks::onActorDie(C_Entity* _this, __int64 a2) {
	static auto oFunc = Hooks::actorDieHook.GetFastcall<void, C_Entity*, __int64>();

	return oFunc(_this, a2);
}

__int64 Hooks::inventoryScreen__tick(C_CraftingScreenController* a1) {
	static auto oFunc = Hooks::inventoryScreen__tickHook.GetFastcall<__int64, C_CraftingScreenController*>();

	return oFunc(a1);
}

int Hooks::ForceThirdPersonLol(__int64 a1) {
	static auto oFunc = Hooks::forceThirdPersonHook.GetFastcall<int, __int64>();

	return oFunc(a1);
}

void Hooks::Actor__Destructor(C_Entity* ent) {
	static auto oFunc = Hooks::Actor__DestructorHook.GetFastcall<void, C_Entity*>();

	return oFunc(ent);
}

int Hooks::inputmode_Hook(C_Entity* ent) {
	static auto oFunc = Hooks::play_inputmodeHook.GetFastcall<int, C_Entity*>();

	return oFunc(ent);
}

std::u8string Hooks::getEdition() {
	static auto oFunc = Hooks::getEditionHook.GetFastcall<std::u8string>();

	return oFunc();
}
